/*
 * $AIST_Release: 0.9.0 $
 * Copyright 2011 Information Technology Research Institute, National
 * Institute of Advanced Industrial Science and Technology
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *    http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package util

import java.lang.Exception
import scala.collection.JavaConversions._
import com.google.protobuf.Descriptors._
import com.google.protobuf.DescriptorProtos._

object PBUtils {

  class PBConvertingException(msgs: String) extends Exception(msgs)

  /**
   * Makes FileDescriptors from FileDescriptorSet.
   */
  def convertFiles(set: FileDescriptorSet, descMap: Map[String, FileDescriptor] = Map()): Map[String, FileDescriptor] = {
    val protoMap = set.getFileList().map{x => (x.getName(), x) }.toMap
    var convertingMap = Map[String, FileDescriptor]()

    def getDesc(name: String) = descMap.get(name).orElse(convertingMap.get(name))

    def convert(converting: Set[String], proto: FileDescriptorProto): FileDescriptor = {
      val name = proto.getName()

      // Ignores if file has a specified name has already read.
      getDesc(name).getOrElse {
        val c = converting + name

        // The process converting form FileDescriptorProto to FileDescriptor 
        // requires FileDescriptors of proto files depended by FileDescriptorProto.
        val deps = proto.getDependencyList().flatMap { fn =>
          getDesc(fn) orElse {
            if (converting.contains(fn)) throw new PBConvertingException("Recursive import \"%s\".".format(fn))
            protoMap.get(fn).map { 
              convert(c, _) 
            } orElse {
              throw new PBConvertingException("Unkown proto file \"%s\". \"%s\" depends on \"%s\".".format(fn, name, fn))
            }
          }
        }

        val desc = FileDescriptor.buildFrom(proto, deps.toArray)
        convertingMap = convertingMap + ((name, desc))

        desc
      }
    }
    
    for (proto <- set.getFileList()) {
      convert(Set(), proto)
    }
    return convertingMap
  }

  def FileDescriptorList2FileDescriptorSet(fds: List[FileDescriptor]): FileDescriptorSet = {

    FileDescriptorSet.newBuilder().addAllFile(fds.map { _.toProto() }).build()
  }

  /**
   * Gets Message Descriptors from FileDescriptor recursively.
   */
  def getMessages(fd: FileDescriptor): List[Descriptor] = {
    def f(d: Descriptor): List[Descriptor] = {
      d::d.getNestedTypes.toList.flatMap(f)
    }
    fd.getMessageTypes.toList.flatMap(f)
  }

  /**
   * Gets Enum Descriptors from FileDescriptor recursively.
   */
  def getEnums(fd: FileDescriptor): List[EnumDescriptor] = {
    def f(d: Descriptor): List[EnumDescriptor] = {
      d.getEnumTypes().toList ++ d.getNestedTypes.toList.flatMap(f)
    }
    fd.getMessageTypes.toList.flatMap(f) ++ fd.getEnumTypes.toList
  }

}
